var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var fs = require('fs');
var path = require('path');

var port = 8080;

server.listen(port);

app.use("/", express.static(__dirname + '/public'));

var filename = "Data.txt";

var enemies = [];
var players = {};

var level = 1;
var map = generateMap(level);

var dodatkoweDoswDobro = 0;
var dodatkoweDoswZlo = 0;

var trees = [];
var stawy = [];

var progiPoziomu = {
    2 : 40,
    3 : 80,
    4 : 400,
    5 : 600,
    6 : 9999
};

var koncowyPoziom = 3;

var listaDobra =  ["wojownik", "ninja", "lucznik"];
var listaZla =    ["wampir", "wilkolak", "nekromanta"];

var zyciePotworow = {};

Array.prototype.contains = function(obj) {
    var i = this.length;
    while (i--) {
        if (this[i] === obj) {
            return true;
        }
    }
    return false;
};

function getPlayerNames(playerDict) {

    var playerNames = [];
    for (var key in playerDict) {
        if (playerDict.hasOwnProperty(key)) {
            playerNames.push(key);
        }
    }
    return playerNames;
}

var sockets = {};

// obsługa połączenia się z klientem
io.sockets.on('connection', function (socket) {

    socket.on('disconnect', function () {

        for (var key in sockets) {
            if (sockets.hasOwnProperty(key)) {

                if (sockets[key] == socket) {

                    console.log("disconnected: ", key);

                    delete sockets[key];
                    delete players[key];
                    return;
                }
            }
        }
    });

    socket.on('wyniki', function(d) {

        fs.readFile(filename, 'utf8', function(err, data) {
            if (err) throw err;
            console.log('OK: ' + filename);
            console.log(data);

            var dane = [];

            var lines = data.split("\n");

            console.log(lines);

            for (var i = 0; i < lines.length; i++) {

                var s = lines[i].split(" ");
                dane.push(s);
                console.log(s);
            }

            socket.emit('wyniki', dane);
        });


    });

    // wysłanie mapy klientowi
    socket.emit('mapa', map);

    // obsługa czatu
    socket.on('msg', function (data) {
        io.sockets.emit('new', data);
    });

    socket.on('playerList', function(data) {

       socket.emit('playerList', getPlayerNames(players));
    });

    // odbiera informacje odnośnie danego gracza
    socket.on('infoAboutPlayer', function (data) {

        if (data.nick.length > 0) {

            players[data.nick] = {

                nick: data.nick,
                nazwa : data.nazwa,
                x : data.x,
                y : data.y,
                zloto : data.zloto,
                dosw: data.dosw,
                czyAtak: data.czyAtak,
                strona: data.strona,
                zycie: data.zycie,
                wspStrzalu: data.wspStrzalu
            };

            sockets[data.nick] = socket;
        }
    });

    socket.on('newGame', function (data) {

        setInterval(function () {
            // wysyła do danego gracza informacje nt innych graczy
            socket.emit('otherPlayers', players);
			
            // oblicza statystyki
            var stats = [0, 0, 0, 0, 0, 0, 0, 0]; // (dobro) ile exp zloto zycie (zlo) ile exp zloto zycie

            var playersPos = [];

            for (var key in players) {
                if (players.hasOwnProperty(key) && key.length > 0) {

                    var p = players[key];
                    if (p.nazwa == "wojownik" || p.nazwa == "lucznik" || p.nazwa == "ninja") {

                        stats[0]++;
                        stats[1] += p.dosw; //dosw
                        stats[2] += p.zloto; //zloto
                        if (p.zycie > 0) stats[3] += p.zycie;

                    } else {

                        stats[4]++;
                        stats[5] += p.dosw; //dosw
                        stats[6] += p.zloto; //zloto
                        if (p.zycie > 0) stats[7] += p.zycie;
                    }

                    playersPos.push([p.x, p.y]);
                }
            }

            stats[1] += dodatkoweDoswDobro;
            stats[5] += dodatkoweDoswZlo;

            if (stats[1] >= progiPoziomu[level + 1] || stats[5] >= progiPoziomu[level + 1]) {

                level++;
                if (level == koncowyPoziom) {

                    var wynik = 0;
                    var rezultat = "REMIS ";
                    if (stats[2] > stats[6]) {
                        wynik = 1;
                        rezultat = "DOBRO ";
                    } else if (stats[2] < stats[6]) {
                        wynik = 2;
                        rezultat = "ZŁO ";
                    }

                    var d = new Date();

                    var dzien = d.getUTCDay() + 1;
                    var miesiac = d.getUTCMonth() + 1;
                    var rok = d.getUTCFullYear();

                    if (dzien < 10) {
                        dzien = "0" + dzien;
                    }

                    if (miesiac < 10) {
                        miesiac = "0" + miesiac;
                    }

                    var s = dzien + "." + miesiac + "." + rok;

                    fs.appendFile(filename, "\n" + rezultat + s, function (err) {

                    });

                    io.sockets.emit('koniecGry', wynik);
                    sockets = [];
                    players = {};
                    enemies = [];
                    return;
                }
                var map = generateMap(level);

                for (var key in players) {
                    if (players.hasOwnProperty(key)) {

                        var p = players[key];

                        if (listaDobra.contains(p.nazwa)) {

                            dodatkoweDoswDobro += 30;
                            break;
                        } else if (listaZla.contains(p.nazwa)) {

                            dodatkoweDoswZlo += 30;
                            break
                        }
                    }
                }

                // wysyłam nową planszę do wszystkich graczy
                io.sockets.emit('newLevel', map);
                return;
            }

            //sprawdzenie interakcji pomiedzy graczami (przeciwnikami)
            for (var key1 in players) {
                if (players.hasOwnProperty(key1) && key1.length > 0) {

                    var p1 = players[key1];

                    for (var key2 in players) {

                        if (players.hasOwnProperty(key2) && key2.length > 0 && key1 !== key2) {

                            var p2 = players[key2];

                            if (Math.abs(p1.x - p2.x) < 30 && Math.abs(p1.y - p2.y) < 30 && p1.czyAtak && p1.strona !== p2.strona) {

                                sockets[p2.nick].emit('loseHP', p1.nick);
                                sockets[p1.nick].emit('miniaturka', {nazwa : p2.nazwa, zycie : p2.zycie})
                            }

                            if (p1.wspStrzalu.length == 2) {
                                console.log(p1.wspStrzalu);

                                var sx = p1.wspStrzalu[0] | 0;
                                var sy = p1.wspStrzalu[1] | 0;

                                if (Math.abs(sx - p2.x) < 30 && Math.abs(sy - p2.y) < 30 && p1.strona !== p2.strona) {

                                    sockets[p2.nick].emit('loseHP', p1.nick);
                                    sockets[p1.nick].emit('miniaturka', {nazwa : p2.nazwa, zycie : p2.zycie})
                                }

                            }
                        }
                    }
                }
            }

            // wysyła statystyki
            socket.emit('teamInfo', stats);

            // oblicza pozycje potworów
            var enemiesData = updateEnemies(enemies, playersPos);

            // wysyła pozycje potworów
            socket.emit('enemiesData', enemiesData);

        }, 200); // co 200ms

        setTimeout(function() {

            for (var key in zyciePotworow) {
                if (zyciePotworow.hasOwnProperty(key)) {
                    var i = parseInt(key);
                    var zycie = zyciePotworow[key];
                    socket.emit('enemyHP', [i, zycie]);
                    console.log([i, zycie]);
                }
            }
        }, 1500);

    });

    socket.on('killer', function(nick) {

        sockets[nick].emit('gainExp', 60);
    });

    socket.on('utrataDosw', function(listaKlas) {

        for (var key in players) {
            if (players.hasOwnProperty(key) && key.length > 0) {

                var p = players[key];

                if (listaKlas.contains(p.nazwa)) {

                    sockets[p.nick].emit('utrataDosw', 0);
                }
            }
        }

        if (listaKlas.contains("wojownik")) {
            dodatkoweDoswDobro = (dodatkoweDoswDobro * 0.9 ) | 0;
        } else {
            dodatkoweDoswZlo = (dodatkoweDoswZlo * 0.9 ) | 0;
        }
    });

    socket.on('ileZycia', function(data) {

        zyciePotworow[data[0]] = data[1];
        socket.broadcast.emit('enemyHP', data);
    });

});

// funkcja oblicza losową wartość całkowitoliczbową z przedziału [min, max] (włącznie)
function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

// aktualizacja pozycji przeciwnikow
// enemies - lista pozycji przeciwników
// playerPos - lista pozycji graczy
function updateEnemies(enemies, playerPos) {

    var res = [];

    for (var i = 0; i < enemies.length; i++) {

        var step = 1;
        // (spowolnienie)
        if (Math.random() > 0.5) {
            step = 0;
        }

        // pobiera pozycje przeciwnikow
        var x = enemies[i][0];
        var y = enemies[i][1];

        // minimalna odgległość od któregoś z graczy
        var closestPdx = 2000;
        var closestPdy = 2000;

        // wspolrzedne najblizszego gracza
        var closestPlayerX = 0;
        var closestPlayerY = 0;

        // w ktora strone ma się poruszać
        var prawo = true;
        var dol = true;

        // o ile ma się poruszyć
        var dx = 0;
        var dy = 0;

        ruch = false;

        // dla każdego gracza
        for (var j = 0; j < playerPos.length; j++) {

            // pobierz współrzedne gracza
            var playerX = playerPos[j][0];
            var playerY = playerPos[j][1];

            // oblicz odległość (wartość bezwzględną od tego gracza)
            var pdx = Math.abs(x - playerX);
            var pdy = Math.abs(y - playerY);

            // jeśli gracz jest za daleko albo za blisko, przejdź do następnego gracza
            if (pdx > 200 || pdy > 200 ||
                pdx < 10 && pdy < 10) {

                continue;
            // jeśli gracz jest w odpowiedniej odległości i żaden wcześniejszy gracz nie był bliżej
            // zapamiętaj dane tego gracza
            } else if (pdx < closestPdx && pdy < closestPdy) {

                // zapamiętujemy dane
                closestPdx = pdx;
                closestPdy = pdy;

                closestPlayerX = playerX;
                closestPlayerY = playerY;

                ruch = true; // ustawiamy flagę gracza, że będzie potrzeba ruchu
            }
        }

        if (x - closestPlayerX < 0) prawo = false; // sprawdzamy czy ruch będzie w prawo czy w lewo
        if (y - closestPlayerY < 0) dol = false; // sprawdzamy czy ruch będzie w dół czy w górę

        var prevX = x;
        var prevY = y;

        if (ruch) {

            if (dol) {
                dy = -step;
            } else {
                dy = step;
            }
            if (prawo) {
                dx = -step;
            } else  {
                dx = step;
            }

            x += dx;
            y += dy;

        }

        if (sprawdzPrzeszkody(x, y)) {
            res.push([prevX, prevY, 0, 0, closestPdx, closestPdy]);
            enemies[i] = [prevX, prevY];
        } else {
            res.push([x, y, dx, dy, closestPdx, closestPdy]);
            enemies[i] = [x, y];

            // tylko jeden potwór naraz się porusza
            if (dx != 0 || dy != 0) {
                step = 0; // reszta juz nie będzie się poruszać
            }
        }
    }

    return res;
}

function randPos(w, h) {

    return [getRandomInt(50, w - 50), getRandomInt(50, h - 50)];
}


function randPair(w, h) {

    return [getRandomInt(0, w), getRandomInt(0, h)];
}

function sprawdzPrzeszkody(x, y) {

    var creatureSize = 100;

    var cx = x + creatureSize/2 | 0;
    var cy = y + creatureSize/2 | 0

    for (var i = 0; i < trees.length; i++) {

        var tx = trees[i][0] + 32;
        var ty = trees[i][1] + 32;

        var dt = Math.sqrt((cx - tx) * (cx - tx) + (cy - ty) * (cy - ty));

        if (dt < 32 + (creatureSize/2 | 0) - 3) {
            return true;
        }
    }

    for (var i = 0; i < stawy.length; i++) {

        var sx = stawy[i][0] + 20;
        var sy = stawy[i][1] + 20;

        var dt = Math.sqrt((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy));


        if (dt < 24 + (creatureSize / 2 | 0) - 3) {
            return true;
        }
    }

    return false;
}

// generuje mapę w zależności od poziomu
function generateMap(level) {

    // ustawia szerokość i wysokość
    var w = 1000 + (level - 1) * 200;
    var h = 1000 + (level - 1) * 200;

    zyciePotworow = {};
    trees = [];

    var h3 = h/3 | 0;

    var c = 400;

    // losowanie polozenia drzew
    for (var i = 0; i < (w / 90) - 2; i++) {

        var r = randPair(25, h3);
        r[0] = r[0] + 90 * i + 30;

        if (Math.abs(r[0]) - c > 70 || Math.abs(r[1] - c) > 70) {
            trees.push(r);
        }

        r = randPair(25, h3);
        r[0] = r[0] + 90 * i + 30;
        r[1] += h3
        if (Math.abs(r[0]) - c > 70 || Math.abs(r[1] - c) > 70) {
            trees.push(r);
        }

        r = randPair(25, h3 - 70);
        r[0] = r[0] + 90 * i + 30;
        r[1] += (2 * h3);
        if (Math.abs(r[0]) - c > 70 || Math.abs(r[1] - c) > 70) {
            trees.push(r);
        }

    }
    stawy = [];
    var malpoludy = [];
    var smoki = [];

    var kopalnie = [];

    var krowy = [];
    var handlarze = [];

    // wyznacza miejsca gdzie mają się pojawić kolejne postaci
    for (var i = 0; i < level; i++) { // level

        malpoludy.push(randPos(w, h));
        smoki.push(randPos(w, h));
    }

    enemies = malpoludy.concat(smoki);

    for (var i = 0; i < 2; i++) {

        kopalnie.push(randPos(w, h));
        stawy.push(randPos(w, h));
    }

    for (var i = 0; i < 4; i++) {

        krowy.push(randPos(w, h));
    }

    for (var i = 0; i < 1; i++) {

        handlarze.push(randPos(w, h));
    }

    return {
        w:w,
        h:h,
        trees:trees,
        malpoludy:malpoludy,
        smoki:smoki,
        kopalnie:kopalnie,
        stawy:stawy,
        krowy:krowy,
        handlarze:handlarze,
        enemies: enemies
    };
}